﻿#if UNITY_NETWORK
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
using UnityEngine.Networking.Types;


namespace Manox.Libs.Networking.Unity
{
    /// <summary>
    /// Unity network client.
    /// </summary>
    public class UnityNetworkClient : UnityNetworkNode, INetworkClient
    {
        /// <summary>Response action.</summary>        
        private Action<INetworkMatch> ResponseAction;
        /// <summary>Response action.</summary>        
        private NetworkResponseDelegate<bool> JoinedAction;
        /// <summary>Connection id.</summary>
        private UnityNetworkConnectionId ConnectionId;
        

        /// <summary>
        /// Get matches.
        /// </summary>
        /// <param name="responseAction"></param>
        public void GetMatches(Action<INetworkMatch> responseAction)
        {
            this.ResponseAction = responseAction;

            this.UnityNetworkMatch.ListMatches(0, 20, "", this.OnMatchList);
        }

        /// <summary>
        /// On match list.
        /// </summary>
        /// <param name="matchListResponse"></param>
        private void OnMatchList(ListMatchResponse matchListResponse)
        {
            if (this.ResponseAction == null)
                return;

            if (matchListResponse.success && matchListResponse.matches != null)
                foreach (INetworkMatch match in this.ConvertToNetworkMatchBase(matchListResponse.matches))
                    this.ResponseAction(match);
                                
            this.ResponseAction = null;
        }

        /// <summary>
        /// Convert to network match base.
        /// </summary>
        /// <param name="matches"></param>
        /// <returns></returns>
        private List<INetworkMatch> ConvertToNetworkMatchBase(List<MatchDesc> matches)
        {
            List<INetworkMatch> output = new List<INetworkMatch>(matches.Count);

            foreach (MatchDesc matchDesc in matches)
                output.Add(new UnityNetworkMatch(matchDesc));
            
            return output;
        }

        /// <summary>
        /// Connect.
        /// </summary>
        /// <param name="match"></param>
        /// <param name="password"></param>
        /// <param name="joinedAction"></param>
        public void Connect(INetworkMatch match, string password, NetworkResponseDelegate<bool> joinedAction)
        {
            this.JoinedAction = joinedAction;

            this.UnityNetworkMatch.JoinMatch((match as UnityNetworkMatch).MatchDesc.networkId, password, this.OnMatchJoined);
        }

        /// <summary>
        /// When we've joined a match we connect to the server/host.
        /// </summary>
        /// <param name="matchJoin"></param>
        public void OnMatchJoined(JoinMatchResponse matchJoin)
        {
            this.MatchInfo = null;

            if (matchJoin.success)
            {
                Debug.Log("Join match succeeded");

                Utility.SetAccessTokenForNetwork(matchJoin.networkId,
                    new NetworkAccessToken(matchJoin.accessTokenString));
                
                this.MatchInfo = new MatchInfo(matchJoin);

                Debug.Log("Connecting to Address:" + matchJoin.address +
                    " Port:" + matchJoin.port +
                    " NetworKID: " + matchJoin.networkId +
                    " NodeID: " + matchJoin.nodeId);

                this.ConnectThroughRelay(matchJoin.address, matchJoin.port, matchJoin.networkId,
                    matchJoin.nodeId);                
            }
            else
                Debug.LogError("Join match failed");                                        
        }

        /// <summary>
        /// Connect through relay server.
        /// </summary>
        /// <param name="relayIp"></param>
        /// <param name="relayPort"></param>
        /// <param name="networkId"></param>
        /// <param name="nodeId"></param>
        void ConnectThroughRelay(string relayIp, int relayPort, NetworkID networkId, NodeID nodeId)
        {
            this.SetupHost(false);

            byte error;

            this.ConnectionId = new UnityNetworkConnectionId(NetworkTransport.ConnectToNetworkPeer(
                this.HostId, relayIp, relayPort, 0, 0, networkId, Utility.GetSourceID(), nodeId, out error));
        }      

        /// <summary>
        /// On connect received.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="channelId"></param>
        /// <param name="buffer"></param>
        /// <param name="receivedSize"></param>
        public override void OnConnectReceived(UnityNetworkConnectionId connectionId, int channelId, byte[] buffer, int receivedSize)
        {
            base.OnConnectReceived(connectionId, channelId, buffer, receivedSize);

            if (this.JoinedAction != null)
                this.JoinedAction(true);
        }

        /// <summary>
        /// Send data.
        /// </summary>        
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        public void Send(byte[] data, int dataLength)
        {
            this.Send(this.ConnectionId, data, dataLength);            
        }
    }
}
#endif